#include <iostream>
#include <tuple>
#include <fstream>
#include <cstring>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <dirent.h>
#include <stack>
#include <thread>
#include <mutex>

#include "parser.hpp"
#include "count.hpp"

using namespace std;

int main(int argc, char *argv[]) {

    auto args = parse_args(argc, argv);
    auto file_list = stack<string>(); // Save fileName in a stack rather than a vector
    DIR* pDir;
    struct dirent* myDirent;
    struct stat s;
    char allName[1024];

    // add clock time
    clock_t start,finish;
	  double totaltime;
	  start=clock();

    for (int i = 1; i < argc; ++i) {
      string t_arg = string(argv[i]);
      if (t_arg == "-A" || t_arg == "--all" || t_arg == "-b" || t_arg == "-r" || t_arg == "-s" || t_arg == "-h" || t_arg == "--help" || t_arg == "-t") // add thread
        continue;
      else if (stat(argv[i],&s)==0){
        if(s.st_mode & S_IFDIR){ // Judge whether it's a file or a dir without opening the dir
          if (args.recursive){ // Recursive searching
            GetListR(argv[i], file_list, args.suffix);
          }
          else{ // Non-recursive searching
            pDir = opendir(argv[i]);
            while((myDirent = readdir(pDir)) != NULL)
	          {
		          if(strcmp(myDirent->d_name, ".") == 0 || strcmp(myDirent->d_name, "..") == 0)
		            {
			            continue;
		            } 
              if (myDirent->d_type == 8){
                sprintf(allName, "%s/%s", argv[i], myDirent->d_name);
                string fileName = allName;
                string fsuffix = fileName.substr(fileName.find_last_of('.') + 1); // Get suffix without '.'
                if (args.suffix == "" || strcmp(fsuffix.c_str(), args.suffix.c_str()) == 0)
                  file_list.emplace(fileName);   
                }
            }
        }
        }
        else if (s.st_mode & S_IFREG){ // It's a regular file
          string fileName = argv[i];
          string fsuffix = fileName.substr(fileName.find_last_of('.') + 1);
          if (args.suffix == "" || strcmp(fsuffix.c_str(), args.suffix.c_str()) == 0)
            file_list.emplace(fileName);}     
        else{
          cout<<"Not a file or a directory!"<<endl;
        }
      }
      else{
        continue;
      }
    }
    // Print fileName and count lines with multi-threads
    if(!args.help){
    cout << "line\tfile" << endl;
    cout<< "----\t----" << endl;

    int allcnts;
    mutex mutex1, mutex2;
    vector<thread> threadPool;
    threadPool.reserve(args.thread);

    //Specify threads
    for (int i = 0; i < args.thread; ++i) {
        // Auto join when deconstruction
        thread t([&]{
            while (true) {
                unique_lock<mutex> grd(mutex1);
                if (file_list.empty())
                {
                  break;
                  }
                string fileName = file_list.top();
                file_list.pop();
                grd.unlock();

                int lineCnts = CountLine(fileName);
                allcnts += lineCnts;
                unique_lock<mutex> grd2(mutex2);
                
                cout << (this_thread::get_id()) << ":" << lineCnts << "\t" << fileName << endl;
            }
        });
        // Extend life cycle
        threadPool.emplace_back(move(t));
}
    // Wait to end
    for (auto &item: threadPool) {
        item.join();
    }

    cout << allcnts << "\ttotal" <<endl;
    }  

    // add clock time
    finish=clock();
   	totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
   	cout<<"\nRunning time: "<<totaltime<<" seconds."<<endl;
    return 0;
}
